#pragma once

#include "../Common/Logger.hpp"
#include "../Common/Tool.hpp"
#include <fcntl.h>
#include <iostream>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>


namespace OJ
{
  class Runner
  {
  public:
    static void setLimit(const int& limit_cup, const int& limit_memory)
    {
      // 设置时间限制
      struct rlimit cpu_lim;
      cpu_lim.rlim_cur = limit_cup;
      cpu_lim.rlim_max = RLIM_INFINITY;
      setrlimit(RLIMIT_CPU, &cpu_lim);
      // 设置空间限制
      struct rlimit memory_lim;
      memory_lim.rlim_cur = limit_memory * 1024;//将byte转换为KB
      memory_lim.rlim_max = RLIM_INFINITY;
      setrlimit(RLIMIT_AS, &memory_lim);
    }

    static int run(const std::string &file_name,const int& limit_cup,const int& limit_memory)
    {
      // 将文件名字转换为可执行文件路径
      std::string execute = Tool::PathBuilder::buildExecutablePath(file_name);
      std::string std_in = Tool::PathBuilder::buildStdInPath(file_name);
      std::string std_out = Tool::PathBuilder::buildStdOutPath(file_name);
      std::string std_err = Tool::PathBuilder::buildStdErrPath(file_name);
      // 创建输入输出文件
      int std_in_fd = open(std_in.c_str(), O_RDONLY | O_CREAT, 0644);
      int std_out_fd = open(std_out.c_str(), O_WRONLY | O_CREAT, 0644);
      int std_err_fd = open(std_err.c_str(), O_WRONLY | O_CREAT, 0644);
      // 判断是否全部打开
      if (std_in_fd < 0 || std_out_fd < 0 || std_err_fd < 0)
      {
        ELOG("文件打开失败");
        closeFd(std_in_fd, std_out_fd, std_err_fd);
        return -1;
      }

      // 创建子进程
      pid_t pid = fork();
      if (pid < 0)
      {
        ELOG("创建子进程失败");
        closeFd(std_in_fd, std_out_fd, std_err_fd);
        return -2;
      }
      else if (pid == 0)
      {
         // 进行重定向
        dup2(std_in_fd, STDIN_FILENO);
        dup2(std_out_fd, STDOUT_FILENO);
        dup2(std_err_fd, STDERR_FILENO);
        //给程序设定时间限制与空间限制
        setLimit(limit_cup, limit_memory);
        execl(execute.c_str(), execute.c_str(), nullptr);
        exit(1);
      }
      else
      {
        //关闭文件
        closeFd(std_in_fd, std_out_fd, std_err_fd);
        // 父进程等待子进程结束
        int status;
        waitpid(pid, &status, 0);
        // 判断是否执行成功
        return (status & 0x7f);
      }
    }

  public:
    static void closeFd(int std_in_fd, int std_out_fd, int std_err_fd)
    {
      close(std_in_fd);
      close(std_out_fd);
      close(std_err_fd);
    }
  };
}
